package com.changge.module.security;

import com.changge.common.models.dto.system.resource.security.RoleSecurityDTO;
import com.changge.module.security.constant.SecurityConst;
import com.changge.common.security.context.SystemContext;
import com.changge.system.facade.service.security.IDynamicSecurityService;
import jakarta.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.authorization.AuthorizationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.web.access.intercept.RequestAuthorizationContext;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.CollectionUtils;

import java.util.Collection;
import java.util.List;
import java.util.function.Supplier;

/**
 * 开放政策代理授权管理器
 *
 * @author zhangrongkang
 * @since 2023/12/6
 */
@Slf4j
@Component
public class OpenPolicyAgentAuthorizationManager implements AuthorizationManager<RequestAuthorizationContext> {

    /**
     * 自定义动态权限
     */
    @Autowired
    private IDynamicSecurityService dynamicSecurityService;

    /**
     * 角色资源列表
     */
    private static List<RoleSecurityDTO> roleSecurityList;

    /**
     * 加载角色资源信息
     * <p>在项目启动时执行该方法，并且将数据保存到内存中</p>
     */
    @PostConstruct
    private void loadResourceData() {
        roleSecurityList = dynamicSecurityService.loadResourceRoleData();
        log.info("load roleSecurityList success...");
    }

    /**
     * 清空当前角色信息权限
     */
    public void clearResourceData() {
        roleSecurityList = null;
    }

    /**
     * 路由匹配工具类
     */
    private final AntPathMatcher antPathMatcher = new AntPathMatcher();

    /**
     * 校验请求用户是否具有访问当前资源权限
     *
     * <p>
     *     这里是将原本 加载元数据 和 权限决策器 整合到了一起，核心思路还是一致的
     * </p>
     *
     * @param authentication 认证对象
     * @param object 请求认证上下文
     * @return AuthorizationDecision(true)--用户具备权限；AuthorizationDecision(false)用户不具备权限
     */
    @Override
    public AuthorizationDecision check(Supplier<Authentication> authentication, RequestAuthorizationContext object) {
        // 如果权限资源集合为空时重新加载一次
        if (CollectionUtils.isEmpty(roleSecurityList)) {
            roleSecurityList = dynamicSecurityService.loadResourceRoleData();
        }
        // 请求的url
        String requestUrl = object.getRequest().getRequestURI();
        for (RoleSecurityDTO roleSecurity : roleSecurityList) {
            // 当前请求Url和数据库中存储的相匹配
            if (antPathMatcher.match(roleSecurity.getUrl(), requestUrl)) {
                // 获取到当前URL需要的角色
                List<String> securityRoles = roleSecurity.getRoles();
                // 如果当前请求不需要角色则放行
                if (CollectionUtils.isEmpty(securityRoles)) {
                    SystemContext.setOperateContent(roleSecurity.getDescription());
                    return new AuthorizationDecision(true);
                }
                // 获取当前用户角色
                Collection<? extends GrantedAuthority> authorities = authentication.get().getAuthorities();
                for (GrantedAuthority authority : authorities) {
                    // 如果当前用户为超级管理员则直接放行
                    if (authority.getAuthority().equals(SecurityConst.ADMIN)) {
                        SystemContext.setOperateContent(roleSecurity.getDescription());
                        return new AuthorizationDecision(true);
                    }
                    for (String securityRole : securityRoles) {
                        // 如果当前用户具有访问角色的资源
                        if (authority.getAuthority().equals(securityRole)) {
                            SystemContext.setOperateContent(roleSecurity.getDescription());
                            return new AuthorizationDecision(true);
                        }
                    }
                }
            }
        }
        log.error("用户不具有访问权限，url: {}", requestUrl);
        // 默认放行不通过
        return new AuthorizationDecision(false);
    }
}
